//
//  XBAudioUnitRecorder.m
//  XBVoiceTool
//
//  Created by xxb on 2018/6/28.
//  Copyright © 2018年 xxb. All rights reserved.
//

#import "XBAudioUnitRecorder.h"
#import "XBAudioTool.h"
#import "XBAudioUploader.h"

#define subPathPCM @"/Documents/xbMedia"
#define stroePath [NSHomeDirectory() stringByAppendingString:subPathPCM]

@interface XBAudioUnitRecorder ()
{
    AudioUnit audioUnit;
}
@property (nonatomic,assign) XBAudioBit bit;
@property (nonatomic,assign) XBAudioRate rate;
@property (nonatomic,assign) XBAudioChannel channel;
@property (nonatomic,assign) AudioStreamBasicDescription inputStreamDesc;
@property(nonatomic,strong) XBAudioUploader * uploader;

@end

@implementation XBAudioUnitRecorder

- (instancetype)initWithRate:(XBAudioRate)rate bit:(XBAudioBit)bit channel:(XBAudioChannel)channel
{
    if (self = [super init])
    {
        self.bit = bit;
        self.rate = rate;
        self.channel = channel;
        
        [self initInputAudioUnitWithRate:self.rate bit:self.bit channel:self.channel];
    }
    return self;
}
- (instancetype)init
{
    if (self = [super init])
    {
        self.bit = XBAudioBit_16;
        self.rate = XBAudioRate_44k;
        self.channel = XBAudioChannel_1;
        
        [self initInputAudioUnitWithRate:self.rate bit:self.bit channel:self.channel];
    }
    return self;
}
- (void)dealloc
{
    [self stop];
    [self checkError:AudioComponentInstanceDispose(audioUnit) operation:
               "AudioComponentInstanceDispose failed"];
//    NSLog(@"XBAudioUnitRecorder销毁");
}

- (void)initInputAudioUnitWithRate:(XBAudioRate)rate bit:(XBAudioBit)bit channel:(XBAudioChannel)channel
{
    //设置AVAudioSession
    NSError *error = nil;
    AVAudioSession* session = [AVAudioSession sharedInstance];
    [session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:&error];
    [session setActive:YES error:nil];
    
    //初始化audioUnit
    AudioComponentDescription inputDesc = [XBAudioTool allocAudioComponentDescriptionWithComponentType:kAudioUnitType_Output componentSubType:kAudioUnitSubType_RemoteIO componentFlags:0 componentFlagsMask:0];
    AudioComponent inputComponent = AudioComponentFindNext(NULL, &inputDesc);
    [self checkError:AudioComponentInstanceNew(inputComponent, &audioUnit) operation:"AudioComponentInstanceNew failure"];
    
    //初始化上传
    self.uploader = [[XBAudioUploader alloc] init];
    self.uploader.path = mp3StroePath;
    
    //设置输出流格式
    int mFramesPerPacket = 1;
    
    AudioStreamBasicDescription inputStreamDesc = [XBAudioTool allocAudioStreamBasicDescriptionWithMFormatID:kAudioFormatLinearPCM mFormatFlags:(kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsNonInterleaved | kAudioFormatFlagIsPacked) mSampleRate:rate mFramesPerPacket:mFramesPerPacket mChannelsPerFrame:channel mBitsPerChannel:bit];
    self.inputStreamDesc = inputStreamDesc;
    
    OSStatus status = AudioUnitSetProperty(audioUnit,
                         kAudioUnitProperty_StreamFormat,
                         kAudioUnitScope_Output,
                         kInputBus,
                         &inputStreamDesc,
                         sizeof(inputStreamDesc));
    [self checkError:status operation:"setProperty inputStreamFormat error"];
    
//    status = AudioUnitSetProperty(audioUnit,
//                                           kAudioUnitProperty_StreamFormat,
//                                           kAudioUnitScope_Input,
//                                           kOutputBus,
//                                           &inputStreamDesc,
//                                           sizeof(inputStreamDesc));
//    [self checkError:status, "setProperty outputStreamFormat error");
    
    
    //麦克风输入设置为1（yes）
    int inputEnable = 1;
    status = AudioUnitSetProperty(audioUnit,
                                  kAudioOutputUnitProperty_EnableIO,
                                  kAudioUnitScope_Input,
                                  kInputBus,
                                  &inputEnable,
                                  sizeof(inputEnable));
    [self checkError:status operation:"setProperty EnableIO error"];
    
    //设置回调
    AURenderCallbackStruct inputCallBackStruce;
    inputCallBackStruce.inputProc = inputCallBackFun;
    inputCallBackStruce.inputProcRefCon = (__bridge void * _Nullable)(self);
    
    status = AudioUnitSetProperty(audioUnit,
                                  kAudioOutputUnitProperty_SetInputCallback,
                                  kAudioUnitScope_Output,
                                  kInputBus,
                                  &inputCallBackStruce,
                                  sizeof(inputCallBackStruce));
    [self checkError:status operation:"setProperty InputCallback error"];
    
    AudioStreamBasicDescription outputDesc0;
    UInt32 size = sizeof(outputDesc0);
    [self checkError:AudioUnitGetProperty(audioUnit,
                                    kAudioUnitProperty_StreamFormat,
                                    kAudioUnitScope_Output,
                                    0,
                                    &outputDesc0,
                                    &size) operation:"get property failure"];
    
    AudioStreamBasicDescription outputDesc1;
    size = sizeof(outputDesc1);
    [self checkError:AudioUnitGetProperty(audioUnit,
                                            kAudioUnitProperty_StreamFormat,
                                            kAudioUnitScope_Input,
                                            0,
                                            &outputDesc1,
                                            &size) operation:"get property failure"];
    
}

-(void)checkError:(OSStatus)error operation:(const char *)operation{
    if (error == noErr) return;
    char errorString[20];
    // See if it appears to be a 4-char-code
    *(UInt32 *)(errorString + 1) = CFSwapInt32HostToBig(error);
    if (isprint(errorString[1]) && isprint(errorString[2]) &&
        isprint(errorString[3]) && isprint(errorString[4])) {
        errorString[0] = errorString[5] = '\'';
        errorString[6] = '\0';
    } else
        // No, format it as an integer
        sprintf(errorString, "%d", (int)error);
    fprintf(stderr, "Error: %s (%s)\n", operation, errorString);
    [XBAudioTool log:XBAudioLogLevelError message:[NSString stringWithFormat:@"录音|失败: Error: %s (%s)", operation, errorString] parameters:nil];
    exit(1);
}

- (void)start
{
    [self delete];
    AudioOutputUnitStart(audioUnit);
    _isRecording = YES;
    [self.uploader start];
}

- (void)stop
{
    [self.uploader stop];
    [self checkError:AudioOutputUnitStop(audioUnit) operation:
               "AudioOutputUnitStop failed"];
    _isRecording = NO;
}

-(void)destory{
    [self stop];
    
}

-(void)setBl_uploaderComplete:(XBAudioUnitRecorderUploaderCompleteBlock)bl_uploaderComplete{
    _bl_uploaderComplete = bl_uploaderComplete;
    
    self.uploader.completeBlock = bl_uploaderComplete;
}

- (AudioStreamBasicDescription)getOutputFormat
{
    return self.inputStreamDesc;
//    AudioStreamBasicDescription outputDesc0;
//    UInt32 size = sizeof(outputDesc0);
//    [self checkError:AudioUnitGetProperty(audioUnit,
//                                    kAudioUnitProperty_StreamFormat,
//                                    kAudioUnitScope_Output,
//                                    0,
//                                    &outputDesc0,
//                                    &size),"get property failure");
//    return outputDesc0;
}

static OSStatus inputCallBackFun(    void *                            inRefCon,
                    AudioUnitRenderActionFlags *    ioActionFlags,
                    const AudioTimeStamp *            inTimeStamp,
                    UInt32                            inBusNumber,
                    UInt32                            inNumberFrames,
                    AudioBufferList * __nullable    ioData)
{

    XBAudioUnitRecorder *recorder = (__bridge XBAudioUnitRecorder *)(inRefCon);
    typeof(recorder) __weak weakRecorder = recorder;
    
    AudioBufferList bufferList;
    bufferList.mNumberBuffers = 1;
    bufferList.mBuffers[0].mData = NULL;
    bufferList.mBuffers[0].mDataByteSize = 0;
    
    AudioUnitRender(recorder->audioUnit,
                    ioActionFlags,
                    inTimeStamp,
                    kInputBus,
                    inNumberFrames,
                    &bufferList);
    
    void    *bufferData = bufferList.mBuffers[0].mData;
    UInt32   bufferSize = bufferList.mBuffers[0].mDataByteSize;
    
    float channelValue[2];
    caculate_bm_db(bufferData, bufferSize, 0, 1, channelValue,true);
    float volLDB = channelValue[0];
//    float volRDB = channelValue[1];
    
    if (recorder.bl_output)
    {
        recorder.bl_output(&bufferList);
    }
    if (recorder.bl_outputFull)
    {
        recorder.bl_outputFull(weakRecorder, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList, volLDB);
    }
    
    return noErr;
}

void caculate_bm_db(void * const data ,size_t length ,int64_t timestamp, NSInteger channel,float channelValue[2],bool isAudioUnit) {
    int16_t *audioData = (int16_t *)data;
    
    if (channel == 1) { // 单声道
        int     sDbChnnel     = 0;
        int16_t curr          = 0;
        int16_t max           = 0;
        size_t traversalTimes = 0;
        
        if (isAudioUnit) {
            traversalTimes = length/2;// 由于512后面的数据显示异常  需要全部忽略掉
        }else{
            traversalTimes = length;
        }
        
        for(int i = 0; i< traversalTimes; i++) {
            curr = *(audioData+i);
            if(curr > max) max = curr;
        }
        
        if(max < 1) {
            sDbChnnel = -100;
        }else {
            sDbChnnel = (20*log10((0.0 + max)/32767) - 0.5);
        }
        
        channelValue[0] = channelValue[1] = sDbChnnel;
        
    } else if (channel == 2){ //双声道
        int sDbChA = 0;
        int sDbChB = 0;
        
        int16_t nCurr[2] = {0};
        int16_t nMax[2] = {0};
        
        for(unsigned int i=0; i<length/2; i++) {
            nCurr[0] = audioData[i];
            nCurr[1] = audioData[i + 1];
            
            if(nMax[0] < nCurr[0]) nMax[0] = nCurr[0];
            
            if(nMax[1] < nCurr[1]) nMax[1] = nCurr[0];
        }
        
        if(nMax[0] < 1) {
            sDbChA = -100;
        } else {
            sDbChA = (20*log10((0.0 + nMax[0])/32767) - 0.5);
        }
        
        if(nMax[1] < 1) {
            sDbChB = -100;
        } else {
            sDbChB = (20*log10((0.0 + nMax[1])/32767) - 0.5);
        }
        
        channelValue[0] = sDbChA;
        channelValue[1] = sDbChB;
    }
}


- (void)delete
{
    NSString *pcmPath = stroePath;
    if ([[NSFileManager defaultManager] fileExistsAtPath:pcmPath])
    {
        [[NSFileManager defaultManager] removeItemAtPath:pcmPath error:nil];
    }
}
@end
